package gtfaces;

import gtfaces.utils.HttpsHack;

import java.util.Map.Entry;
import org.jsoup.*;
import org.jsoup.Connection.*;
import org.jsoup.nodes.*;
import org.jsoup.select.*;
import java.util.*;

public class FacesHandler extends Thread {
	
	public static boolean DEBUG;
	
	public static int _25SEC_TIMEOUT;
	
	static {
		DEBUG = false;
		_25SEC_TIMEOUT = 25000;
	}
	
	private Map<String, String> cookies;
	private Map<String, String> headers;
	
	private String userName;
	private String password;
	private static int total, current;
	
	public synchronized void addWork(int work) {
		total += work;
	}
	
	public synchronized void finishedWorkUnit() {
		current++;
		if (DEBUG)
			System.out.println("\t" + current + "/" + total);
	}

	private Map<String, RunResults> rrMap;
	
	public Map<String, String> getHeaders() {
		return headers;
	}
	
	public Map<String, RunResults> getRunResults() {
		return rrMap;
	}
	
	public double getProgress() {
		if (total == 0)
			return 0.1;
		return (double)current / (double)total;
	}
	
	public String getResultsString() {
		StringBuilder sb = new StringBuilder();
		for(TSquareClassExplorer exp : TSquareClassExplorer.allTSquareClasses)
			sb.append(exp.getSuccessString()).append("\n");
		return sb.toString();
	}
	
	public FacesHandler(String userName, String password) {
		super(userName);
		cookies = new HashMap<String, String>();
		headers = new HashMap<String, String>();
		headers.put("Cache-Control", "no-cache");
		headers.put("Pragma", "no-cache");
		this.userName = userName;
		this.password = password;
		total = 0;
		rrMap = new HashMap<String, RunResults>();
		HttpsHack.resetCertificateValidation();
	}
	
	@Override
	public void run() {
		Map<String, String> data = new HashMap<String,String>();
		try {
			// Goto T-Square
			Document doc = send("http://t-square.gatech.edu/", null, true);
			Elements eles = doc.select("a:containsOwn(Login)");
			// Goto Login
			doc = send(eles.get(0).attr("href"), null, true);
			eles = doc.select("form[id=fm1]");
			// Submit login form
			String loginAction = doc.baseUri();
			data.put("username", userName);
			data.put("password", password);
			data.put("warn", "true");
			data.put("lt","e1s1");
			data.put("_eventId","submit");
			// Submit login form
			doc = post(loginAction, data);
			// Get T-Square Course Links
			eles = doc.select("a[title~=[A-Z]{2} ?- ?\\d{4}.*]");
			// Visit each link for a class roster
			for (Element ele : eles) {
				RunResults rResults = new RunResults(ele.attr("title"));
				rrMap.put(ele.attr("title"), rResults);
				new TSquareClassExplorer(ele.attr("title"), ele.attr("href"), deepCopy(), rResults).start();
			}
			for (TSquareClassExplorer t : TSquareClassExplorer.allTSquareClasses) {
				t.join();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void handleHeaderCookies(Response response) {
		if (response != null && response.getReq() != null && response.getReq().cookies() != null) {
			StringBuilder sb = new StringBuilder();
			for (Entry<String, String> cookie : response.getReq().cookies().entrySet())
    	        sb.append(cookie.getKey()).append("=").append(cookie.getValue()).append("; ");
    	    if (sb.length() > 0)
    	    	sb.delete(sb.length()-2, sb.length());
    	    headers.put("Cookie", sb.toString());
		}
	}
	
	public Document send(String url, Map<String, String> data, boolean updateHeaderCookies) throws Exception {
		return send(url, data, Method.GET, updateHeaderCookies);
	}
	
	public Document send(String url, Map<String, String> data, Method meth, boolean updateHeaderCookies) throws Exception {
	    Connection connection = Jsoup.connect(url).followRedirects(true);
	    connection.timeout(_25SEC_TIMEOUT).method(meth);
	    for (Entry<String, String> cookie : cookies.entrySet())
	        connection.cookie(cookie.getKey(), cookie.getValue());
	    for (Entry<String, String> header : headers.entrySet())
	        connection.header(header.getKey(), header.getValue());
	    
	    if (DEBUG) {
		    System.out.println(">>>"+url);
		    System.out.println("\t"+cookies);
		    System.out.println("\t"+headers);
	    }
	    
	    if (data != null)
	    	for (Entry<String, String> me : data.entrySet())
	    		connection.data(me.getKey(), me.getValue());
	    Response response = connection.execute();
	    cookies.putAll(response.cookies());
	    if (updateHeaderCookies)
	    	handleHeaderCookies(response);
	    return response.parse();
	}

	public Document post(String url, Map<String, String> data) {
	    Connection connection = Jsoup.connect(url).followRedirects(true);
	    for (Entry<String, String> cookie : cookies.entrySet())
	        connection.cookie(cookie.getKey(), cookie.getValue());
	    for (Entry<String, String> header : headers.entrySet())
	        connection.header(header.getKey(), header.getValue());
	    if (data != null)
	    	for (Entry<String, String> me : data.entrySet())
	    		connection.data(me.getKey(), me.getValue());
	    try {
	    	Response response = connection.execute();
		    cookies.putAll(response.cookies());
		    return response.parse();
	    } catch (Exception e) {
	    	try {
	    		String newUrl = "https://t-square.gatech.edu/portal";
	    		connection = Jsoup.connect(newUrl).followRedirects(true);
	    	    for (Entry<String, String> cookie : cookies.entrySet())
	    	        connection.cookie(cookie.getKey(), cookie.getValue());
	    	    for (Entry<String, String> header : headers.entrySet())
	    	        connection.header(header.getKey(), header.getValue());
	    		Response response = connection.execute();
			    cookies.putAll(response.cookies());
			    return response.parse();
	    	} catch (Exception j) { j.printStackTrace(); }
	    	return null;
	    }
	}
	
	public FacesHandler deepCopy() {
		FacesHandler newPOC = new FacesHandler(this.userName, this.password);
		Map<String, String> nCookies = new HashMap<String, String>();
		Map<String, String> nHeaders = new HashMap<String, String>();
		nCookies.putAll(cookies);
		nHeaders.putAll(headers);
		newPOC.cookies = nCookies;
		newPOC.headers = nHeaders;
		return newPOC;
	}
}
